/**
 * 可访问性检查工具
 * 用于检测和报告网站的可访问性问题
 */

/**
 * 可访问性问题类型
 */
export interface AccessibilityIssue {
  type: 'error' | 'warning' | 'info';
  rule: string;
  message: string;
  element?: Element;
  selector?: string;
  impact: 'critical' | 'serious' | 'moderate' | 'minor';
  wcagLevel: 'A' | 'AA' | 'AAA';
  wcagCriteria: string[];
}

/**
 * 可访问性检查结果
 */
export interface AccessibilityReport {
  url: string;
  timestamp: number;
  issues: AccessibilityIssue[];
  summary: {
    total: number;
    errors: number;
    warnings: number;
    info: number;
  };
  score: number; // 0-100
}

/**
 * 可访问性检查器类
 */
export class AccessibilityChecker {
  private issues: AccessibilityIssue[] = [];

  /**
   * 运行完整的可访问性检查
   */
  async runFullCheck(): Promise<AccessibilityReport> {
    this.issues = [];

    // 运行各项检查
    this.checkImages();
    this.checkHeadings();
    this.checkLinks();
    this.checkForms();
    this.checkButtons();
    this.checkKeyboardNavigation();
    this.checkColorContrast();
    this.checkAriaLabels();
    this.checkLandmarks();
    this.checkFocus();
    this.checkLanguage();
    this.checkTables();

    return this.generateReport();
  }

  /**
   * 检查图片可访问性
   */
  private checkImages(): void {
    const images = document.querySelectorAll('img');

    images.forEach((img, index) => {
      const alt = img.getAttribute('alt');
      const src = img.getAttribute('src');

      // 检查缺失的alt属性
      if (alt === null) {
        this.addIssue({
          type: 'error',
          rule: 'img-alt',
          message: '图片缺少alt属性',
          element: img,
          selector: `img:nth-child(${index + 1})`,
          impact: 'critical',
          wcagLevel: 'A',
          wcagCriteria: ['1.1.1'],
        });
      }

      // 检查空的alt属性（装饰性图片应该有空alt）
      else if (alt === '' && !this.isDecorativeImage(img)) {
        this.addIssue({
          type: 'warning',
          rule: 'img-alt-empty',
          message: '图片alt属性为空，请确认这是装饰性图片',
          element: img,
          selector: `img:nth-child(${index + 1})`,
          impact: 'moderate',
          wcagLevel: 'A',
          wcagCriteria: ['1.1.1'],
        });
      }

      // 检查alt文本质量
      else if (alt && (alt.length > 125 || this.hasRedundantText(alt))) {
        this.addIssue({
          type: 'warning',
          rule: 'img-alt-quality',
          message: 'alt文本过长或包含冗余信息',
          element: img,
          selector: `img:nth-child(${index + 1})`,
          impact: 'minor',
          wcagLevel: 'A',
          wcagCriteria: ['1.1.1'],
        });
      }

      // 检查缺失的src属性
      if (!src) {
        this.addIssue({
          type: 'error',
          rule: 'img-src',
          message: '图片缺少src属性',
          element: img,
          selector: `img:nth-child(${index + 1})`,
          impact: 'serious',
          wcagLevel: 'A',
          wcagCriteria: ['1.1.1'],
        });
      }
    });
  }

  /**
   * 检查标题结构
   */
  private checkHeadings(): void {
    const headings = document.querySelectorAll('h1, h2, h3, h4, h5, h6');
    let previousLevel = 0;
    let hasH1 = false;

    headings.forEach((heading, index) => {
      const level = parseInt(heading.tagName.charAt(1));

      // 检查是否有h1
      if (level === 1) {
        if (hasH1) {
          this.addIssue({
            type: 'warning',
            rule: 'heading-multiple-h1',
            message: '页面包含多个h1标题',
            element: heading,
            selector: heading.tagName.toLowerCase(),
            impact: 'moderate',
            wcagLevel: 'AA',
            wcagCriteria: ['2.4.6'],
          });
        }
        hasH1 = true;
      }

      // 检查标题层级跳跃
      if (previousLevel > 0 && level > previousLevel + 1) {
        this.addIssue({
          type: 'warning',
          rule: 'heading-skip-level',
          message: `标题层级跳跃：从h${previousLevel}直接跳到h${level}`,
          element: heading,
          selector: `${heading.tagName.toLowerCase()}:nth-child(${index + 1})`,
          impact: 'moderate',
          wcagLevel: 'AA',
          wcagCriteria: ['2.4.6'],
        });
      }

      // 检查空标题
      if (!heading.textContent?.trim()) {
        this.addIssue({
          type: 'error',
          rule: 'heading-empty',
          message: '标题内容为空',
          element: heading,
          selector: `${heading.tagName.toLowerCase()}:nth-child(${index + 1})`,
          impact: 'serious',
          wcagLevel: 'A',
          wcagCriteria: ['2.4.6'],
        });
      }

      previousLevel = level;
    });

    // 检查页面是否缺少h1
    if (!hasH1) {
      this.addIssue({
        type: 'error',
        rule: 'heading-missing-h1',
        message: '页面缺少h1标题',
        impact: 'serious',
        wcagLevel: 'AA',
        wcagCriteria: ['2.4.6'],
      });
    }
  }

  /**
   * 检查链接可访问性
   */
  private checkLinks(): void {
    const links = document.querySelectorAll('a');

    links.forEach((link, index) => {
      const href = link.getAttribute('href');
      const text = link.textContent?.trim();
      const ariaLabel = link.getAttribute('aria-label');

      // 检查缺失的href
      if (!href) {
        this.addIssue({
          type: 'warning',
          rule: 'link-href',
          message: '链接缺少href属性',
          element: link,
          selector: `a:nth-child(${index + 1})`,
          impact: 'moderate',
          wcagLevel: 'A',
          wcagCriteria: ['2.1.1'],
        });
      }

      // 检查链接文本
      if (!text && !ariaLabel) {
        this.addIssue({
          type: 'error',
          rule: 'link-text',
          message: '链接缺少可访问的文本',
          element: link,
          selector: `a:nth-child(${index + 1})`,
          impact: 'critical',
          wcagLevel: 'A',
          wcagCriteria: ['2.4.4'],
        });
      }

      // 检查模糊的链接文本
      if (text && this.isVagueLinkText(text)) {
        this.addIssue({
          type: 'warning',
          rule: 'link-text-vague',
          message: '链接文本过于模糊，应该更具描述性',
          element: link,
          selector: `a:nth-child(${index + 1})`,
          impact: 'moderate',
          wcagLevel: 'AA',
          wcagCriteria: ['2.4.4'],
        });
      }

      // 检查外部链接
      if (href && this.isExternalLink(href) && !link.getAttribute('target')) {
        this.addIssue({
          type: 'info',
          rule: 'link-external',
          message: '外部链接建议添加target="_blank"和rel="noopener"',
          element: link,
          selector: `a:nth-child(${index + 1})`,
          impact: 'minor',
          wcagLevel: 'AAA',
          wcagCriteria: ['3.2.1'],
        });
      }
    });
  }

  /**
   * 检查表单可访问性
   */
  private checkForms(): void {
    const inputs = document.querySelectorAll('input, textarea, select');

    inputs.forEach((input, index) => {
      const id = input.getAttribute('id');
      const label = id ? document.querySelector(`label[for="${id}"]`) : null;
      const ariaLabel = input.getAttribute('aria-label');
      const ariaLabelledby = input.getAttribute('aria-labelledby');

      // 检查表单控件标签
      if (!label && !ariaLabel && !ariaLabelledby) {
        this.addIssue({
          type: 'error',
          rule: 'form-label',
          message: '表单控件缺少标签',
          element: input,
          selector: `${input.tagName.toLowerCase()}:nth-child(${index + 1})`,
          impact: 'critical',
          wcagLevel: 'A',
          wcagCriteria: ['1.3.1', '3.3.2'],
        });
      }

      // 检查必填字段标识
      if (
        input.hasAttribute('required') &&
        !input.getAttribute('aria-required')
      ) {
        this.addIssue({
          type: 'warning',
          rule: 'form-required',
          message: '必填字段建议添加aria-required="true"',
          element: input,
          selector: `${input.tagName.toLowerCase()}:nth-child(${index + 1})`,
          impact: 'moderate',
          wcagLevel: 'A',
          wcagCriteria: ['3.3.2'],
        });
      }
    });
  }

  /**
   * 检查按钮可访问性
   */
  private checkButtons(): void {
    const buttons = document.querySelectorAll(
      'button, input[type="button"], input[type="submit"]'
    );

    buttons.forEach((button, index) => {
      const text = button.textContent?.trim();
      const ariaLabel = button.getAttribute('aria-label');
      const value = button.getAttribute('value');

      // 检查按钮文本
      if (!text && !ariaLabel && !value) {
        this.addIssue({
          type: 'error',
          rule: 'button-text',
          message: '按钮缺少可访问的文本',
          element: button,
          selector: `button:nth-child(${index + 1})`,
          impact: 'critical',
          wcagLevel: 'A',
          wcagCriteria: ['2.4.4'],
        });
      }
    });
  }

  /**
   * 检查键盘导航
   */
  private checkKeyboardNavigation(): void {
    const interactiveElements = document.querySelectorAll(
      'a, button, input, textarea, select, [tabindex]'
    );

    interactiveElements.forEach((element, index) => {
      const tabindex = element.getAttribute('tabindex');

      // 检查负tabindex
      if (tabindex && parseInt(tabindex) < -1) {
        this.addIssue({
          type: 'warning',
          rule: 'keyboard-tabindex',
          message: 'tabindex值不应小于-1',
          element: element,
          selector: `${element.tagName.toLowerCase()}:nth-child(${index + 1})`,
          impact: 'moderate',
          wcagLevel: 'A',
          wcagCriteria: ['2.1.1'],
        });
      }

      // 检查正tabindex（不推荐）
      if (tabindex && parseInt(tabindex) > 0) {
        this.addIssue({
          type: 'warning',
          rule: 'keyboard-positive-tabindex',
          message: '避免使用正数tabindex，可能破坏自然的tab顺序',
          element: element,
          selector: `${element.tagName.toLowerCase()}:nth-child(${index + 1})`,
          impact: 'moderate',
          wcagLevel: 'A',
          wcagCriteria: ['2.4.3'],
        });
      }
    });
  }

  /**
   * 检查颜色对比度
   */
  private checkColorContrast(): void {
    const textElements = document.querySelectorAll(
      'p, span, div, h1, h2, h3, h4, h5, h6, a, button'
    );

    textElements.forEach((element, index) => {
      if (!element.textContent?.trim()) return;

      const styles = window.getComputedStyle(element);
      const color = styles.color;
      const backgroundColor = styles.backgroundColor;

      // 简单的对比度检查（实际应用中需要更复杂的算法）
      if (this.hasLowContrast(color, backgroundColor)) {
        this.addIssue({
          type: 'warning',
          rule: 'color-contrast',
          message: '文本颜色对比度可能不足',
          element: element,
          selector: `${element.tagName.toLowerCase()}:nth-child(${index + 1})`,
          impact: 'moderate',
          wcagLevel: 'AA',
          wcagCriteria: ['1.4.3'],
        });
      }
    });
  }

  /**
   * 检查ARIA标签
   */
  private checkAriaLabels(): void {
    const elementsWithAria = document.querySelectorAll(
      '[aria-label], [aria-labelledby], [aria-describedby]'
    );

    elementsWithAria.forEach((element, index) => {
      const ariaLabel = element.getAttribute('aria-label');
      const ariaLabelledby = element.getAttribute('aria-labelledby');

      // 检查空的aria-label
      if (ariaLabel === '') {
        this.addIssue({
          type: 'error',
          rule: 'aria-label-empty',
          message: 'aria-label属性不应为空',
          element: element,
          selector: `${element.tagName.toLowerCase()}:nth-child(${index + 1})`,
          impact: 'serious',
          wcagLevel: 'A',
          wcagCriteria: ['1.3.1'],
        });
      }

      // 检查aria-labelledby引用的元素是否存在
      if (ariaLabelledby) {
        const referencedElement = document.getElementById(ariaLabelledby);
        if (!referencedElement) {
          this.addIssue({
            type: 'error',
            rule: 'aria-labelledby-invalid',
            message: 'aria-labelledby引用的元素不存在',
            element: element,
            selector: `${element.tagName.toLowerCase()}:nth-child(${index + 1})`,
            impact: 'serious',
            wcagLevel: 'A',
            wcagCriteria: ['1.3.1'],
          });
        }
      }
    });
  }

  /**
   * 检查页面地标
   */
  private checkLandmarks(): void {
    const main = document.querySelector('main');
    const nav = document.querySelector('nav');

    if (!main) {
      this.addIssue({
        type: 'warning',
        rule: 'landmark-main',
        message: '页面缺少main地标',
        impact: 'moderate',
        wcagLevel: 'AA',
        wcagCriteria: ['2.4.1'],
      });
    }

    if (!nav) {
      this.addIssue({
        type: 'info',
        rule: 'landmark-nav',
        message: '页面建议包含nav地标',
        impact: 'minor',
        wcagLevel: 'AA',
        wcagCriteria: ['2.4.1'],
      });
    }
  }

  /**
   * 检查焦点管理
   */
  private checkFocus(): void {
    const focusableElements = document.querySelectorAll(
      'a, button, input, textarea, select, [tabindex]:not([tabindex="-1"])'
    );

    // 检查是否有可聚焦元素
    if (focusableElements.length === 0) {
      this.addIssue({
        type: 'warning',
        rule: 'focus-no-focusable',
        message: '页面没有可聚焦的元素',
        impact: 'serious',
        wcagLevel: 'A',
        wcagCriteria: ['2.1.1'],
      });
    }
  }

  /**
   * 检查语言设置
   */
  private checkLanguage(): void {
    const html = document.documentElement;
    const lang = html.getAttribute('lang');

    if (!lang) {
      this.addIssue({
        type: 'error',
        rule: 'html-lang',
        message: 'html元素缺少lang属性',
        element: html,
        selector: 'html',
        impact: 'serious',
        wcagLevel: 'A',
        wcagCriteria: ['3.1.1'],
      });
    }
  }

  /**
   * 检查表格可访问性
   */
  private checkTables(): void {
    const tables = document.querySelectorAll('table');

    tables.forEach((table, index) => {
      const caption = table.querySelector('caption');
      const headers = table.querySelectorAll('th');

      // 检查表格标题
      if (!caption && !table.getAttribute('aria-label')) {
        this.addIssue({
          type: 'warning',
          rule: 'table-caption',
          message: '表格建议包含caption或aria-label',
          element: table,
          selector: `table:nth-child(${index + 1})`,
          impact: 'moderate',
          wcagLevel: 'A',
          wcagCriteria: ['1.3.1'],
        });
      }

      // 检查表头
      if (headers.length === 0) {
        this.addIssue({
          type: 'warning',
          rule: 'table-headers',
          message: '表格缺少表头（th元素）',
          element: table,
          selector: `table:nth-child(${index + 1})`,
          impact: 'moderate',
          wcagLevel: 'A',
          wcagCriteria: ['1.3.1'],
        });
      }
    });
  }

  /**
   * 添加问题到列表
   */
  private addIssue(issue: AccessibilityIssue): void {
    this.issues.push(issue);
  }

  /**
   * 生成检查报告
   */
  private generateReport(): AccessibilityReport {
    const summary = {
      total: this.issues.length,
      errors: this.issues.filter(issue => issue.type === 'error').length,
      warnings: this.issues.filter(issue => issue.type === 'warning').length,
      info: this.issues.filter(issue => issue.type === 'info').length,
    };

    // 计算可访问性分数
    const score = this.calculateScore(summary);

    return {
      url: window.location.href,
      timestamp: Date.now(),
      issues: this.issues,
      summary,
      score,
    };
  }

  /**
   * 计算可访问性分数
   */
  private calculateScore(summary: {
    errors: number;
    warnings: number;
    info: number;
  }): number {
    const maxScore = 100;
    const errorPenalty = 10;
    const warningPenalty = 3;
    const infoPenalty = 1;

    const penalty =
      summary.errors * errorPenalty +
      summary.warnings * warningPenalty +
      summary.info * infoPenalty;

    return Math.max(0, maxScore - penalty);
  }

  /**
   * 辅助方法：检查是否为装饰性图片
   */
  private isDecorativeImage(img: Element): boolean {
    const role = img.getAttribute('role');
    const parent = img.parentElement;

    return (
      role === 'presentation' ||
      role === 'none' ||
      parent?.classList.contains('decoration') ||
      parent?.classList.contains('background')
    );
  }

  /**
   * 辅助方法：检查alt文本是否包含冗余信息
   */
  private hasRedundantText(alt: string): boolean {
    const redundantWords = [
      'image',
      'picture',
      'photo',
      'graphic',
      '图片',
      '图像',
      '照片',
    ];
    return redundantWords.some(word =>
      alt.toLowerCase().includes(word.toLowerCase())
    );
  }

  /**
   * 辅助方法：检查是否为模糊的链接文本
   */
  private isVagueLinkText(text: string): boolean {
    const vagueTexts = [
      'click here',
      'read more',
      'more',
      'here',
      '点击这里',
      '更多',
      '详情',
    ];
    return vagueTexts.includes(text.toLowerCase().trim());
  }

  /**
   * 辅助方法：检查是否为外部链接
   */
  private isExternalLink(href: string): boolean {
    try {
      const url = new URL(href, window.location.origin);
      return url.origin !== window.location.origin;
    } catch {
      return false;
    }
  }

  /**
   * 辅助方法：检查颜色对比度（简化版）
   */
  private hasLowContrast(color: string, backgroundColor: string): boolean {
    // 这里应该实现真正的对比度计算算法
    // 简化版本，实际应用中需要更复杂的实现
    return false;
  }
}

/**
 * 运行可访问性检查
 */
export async function runAccessibilityCheck(): Promise<AccessibilityReport> {
  const checker = new AccessibilityChecker();
  return await checker.runFullCheck();
}

/**
 * 在开发环境中自动运行可访问性检查
 */
export function enableDevelopmentAccessibilityCheck(): void {
  if (process.env.NODE_ENV === 'development' && typeof window !== 'undefined') {
    // 页面加载完成后运行检查
    window.addEventListener('load', async () => {
      try {
        const report = await runAccessibilityCheck();

        if (report.issues.length > 0) {
          console.group('🔍 可访问性检查报告');
          console.log(`分数: ${report.score}/100`);
          console.log(`问题总数: ${report.summary.total}`);
          console.log(`错误: ${report.summary.errors}`);
          console.log(`警告: ${report.summary.warnings}`);
          console.log(`信息: ${report.summary.info}`);

          report.issues.forEach((issue, index) => {
            const emoji =
              issue.type === 'error'
                ? '❌'
                : issue.type === 'warning'
                  ? '⚠️'
                  : 'ℹ️';
            console.log(
              `${emoji} ${index + 1}. ${issue.message} (${issue.rule})`
            );
            if (issue.element) {
              console.log('   元素:', issue.element);
            }
          });

          console.groupEnd();
        } else {
          console.log('✅ 可访问性检查通过，未发现问题');
        }
      } catch (error) {
        console.error('可访问性检查失败:', error);
      }
    });
  }
}
